//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <mdetect.h>

//
// Mutex
//
DzMutex::DzMutex()
{
    m_uNested = 0;

    m_pUndoOperationOwner = NULL;
}

DzMutex::~DzMutex()
{
    MDETECT_DETACH_FROM_MUTEX_LIST

    m_waitListLock.Lock();
    if (NULL != UndoOperation::m_pNext) {
        UndoOperation::Detach();
    }
    m_waitListLock.Unlock();
}

// IMPORTANT: when own the m_waitListLock
INLINE void DzMutex::AddMutexUndoOperation()
{
    register Thread *pCurrentThread = GetCurrentThread();

    if (NULL != UndoOperation::m_pNext) {
        UndoOperation::Detach();
    }

    m_pUndoOperationOwner = pCurrentThread;
    m_bUndoOwner = TRUE;
    pCurrentThread->AddUndoOperation(this);
}

INLINE void DzMutex::RemoveMutexUndoOperation()
{
    register Thread *pCurrentThread = GetCurrentThread();

    bool_t bOriginalPreemption = DzDisablePreemption();
    if (m_pUndoOperationOwner == pCurrentThread) {
        pCurrentThread->RemoveUndoOperation(this);
        m_pUndoOperationOwner = NULL;
    }
    DzRestorePreemption(bOriginalPreemption);
}

bool_t DzMutex::IsHeld() const
{
    return (m_pOwner == GetCurrentThread());
}

bool_t DzMutex::IsHeldOnce() const
{
    assert(IsHeld());
    return (1 == m_uNested);
}

void DzMutex::UninterruptibleLock()
{
    MDETECT_DEADLOCK_DETECTION

    m_waitListLock.Lock();
    if (NULL == InterlockedCompareExchangePointer(
            (void **)&m_pOwner, GetCurrentThread(), NULL)) {
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    else if (m_pOwner != GetCurrentThread()) {
        while (NULL != InterlockedCompareExchangePointer(
                (void **)&m_pOwner, GetCurrentThread(), NULL)) {
            WaiterReadyToUninterruptibleSleep();
            m_waitListLock.Unlock();

            Schedule();

            m_waitListLock.Lock();
        }
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    m_waitListLock.Unlock();

    m_uNested++;
}

void DzMutex::Lock(WaitResult *pResult)
{
    assert(pResult);

    MDETECT_DEADLOCK_DETECTION

    m_waitListLock.Lock();
    if (NULL == InterlockedCompareExchangePointer(
            (void **)&m_pOwner, GetCurrentThread(), NULL)) {
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    else if (m_pOwner != GetCurrentThread()) {
        while (NULL != InterlockedCompareExchangePointer(
                (void **)&m_pOwner, GetCurrentThread(), NULL)) {
            WaiterReadyToSleep(pResult);
            if (WaitResult_OK != *pResult) {
                m_waitListLock.Unlock();
                return;
            }
            m_waitListLock.Unlock();

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            m_waitListLock.Lock();
        }
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    m_waitListLock.Unlock();

    m_uNested++;
    *pResult = WaitResult_OK;
}

void DzMutex::TryUninterruptibleLock(uint_t uTicks, WaitResult *pResult)
{
    assert(pResult);

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    m_waitListLock.Lock();
    if (NULL == InterlockedCompareExchangePointer(
            (void **)&m_pOwner, GetCurrentThread(), NULL)) {
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    else if (m_pOwner != GetCurrentThread()) {
        while (NULL != InterlockedCompareExchangePointer(
                (void **)&m_pOwner, GetCurrentThread(), NULL)) {
            if (0 == uTicks) {
                m_waitListLock.Unlock();
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(uTicks);
            WaiterReadyToUninterruptibleSleep(&timer);
            m_waitListLock.Unlock();

            Schedule();

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            uTicks = timer.GetResidualTicks();

            m_waitListLock.Lock();
        }
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    m_waitListLock.Unlock();

    m_uNested++;
    *pResult = WaitResult_OK;
}

void DzMutex::TryLock(uint_t uTicks, WaitResult *pResult)
{
    assert(pResult);

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    m_waitListLock.Lock();
    if (NULL == InterlockedCompareExchangePointer(
            (void **)&m_pOwner, GetCurrentThread(), NULL)) {
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    else if (m_pOwner != GetCurrentThread()) {
        while (NULL != InterlockedCompareExchangePointer(
                (void **)&m_pOwner, GetCurrentThread(), NULL)) {
            if (0 == uTicks) {
                m_waitListLock.Unlock();
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(uTicks);
            WaiterReadyToSleep(&timer, pResult);
            if (WaitResult_OK != *pResult) {
                m_waitListLock.Unlock();
                return;
            }
            m_waitListLock.Unlock();

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            uTicks = timer.GetResidualTicks();

            m_waitListLock.Lock();
        }
        m_pOwner->AddOwnedSyncObject(this);
        AddMutexUndoOperation();
    }
    m_waitListLock.Unlock();

    m_uNested++;
    *pResult = WaitResult_OK;
}

void DzMutex::Unlock()
{
    assert(m_pOwner == GetCurrentThread());

    m_uNested--;
    if (0 == m_uNested) {
        m_waitListLock.Lock();
        m_bUndoOwner = FALSE;
        ClearOwner();
        m_waitListLock.Unlock();

        WakeUpSomeWaiters(TRUE);

        RemoveMutexUndoOperation();
    }
}

// IMPORTANT: when disable preemption
void DzMutex::UnlockBegin()
{
    assert(m_pOwner == GetCurrentThread());
    assert(1 == m_uNested);

    m_uNested--;

    m_bUndoOwner = FALSE;
    ClearOwner();

    BeginToWakeUpSomeWaiters();
}

void DzMutex::UnlockEnd()
{
    WaiveInheritedPriority();

    RemoveMutexUndoOperation();
}

void DzMutex::Undo(Thread *pThread)
{
    if (m_pUndoOperationOwner != pThread) return;

    bool_t bOriginalPreemption = DzDisablePreemption();

    m_pUndoOperationOwner = NULL;

    if (m_bUndoOwner) {
        m_uNested = 0;
        ClearOwner();
    }

    DzRestorePreemption(bOriginalPreemption);

    WakeUpSomeWaiters(TRUE);
}
